package cn.xy.basic.service.impl;

import cn.xy.basic.constants.BaseConstant;
import cn.xy.basic.dto.RegisterDto;
import cn.xy.basic.exception.BusinessException;
import cn.xy.basic.service.IVerifyCodeService;
import cn.xy.basic.util.SmsUtil;
import cn.xy.basic.util.StrUtils;
import cn.xy.basic.util.VerifyCodeUtils;
import cn.xy.user.domain.User;
import cn.xy.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;
@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String imageCode(String key) {
        //先判断前端传来的key值不为空，为空抛异常
        if(StringUtils.isEmpty(key)){
            throw new BusinessException("key不能为空");
        }
        //生成验证码,设置验证码长度为4.
        String code = StrUtils.getComplexRandomString(4);
        //将验证码和前端的key传入redis中，设置保存时间，
        redisTemplate.opsForValue().set(key,code,5, TimeUnit.MINUTES);
        //使用2D引擎把code转为图片，再将图使用Base64转换为字符串并返回，
        return VerifyCodeUtils.VerifyCode(100,40,code);

    }

    /**
     * 短信验证码
     * @param dto
     */
    @Override
    public void smsCode(RegisterDto dto) {
        //获取手机号，图片验证码，图片验证码的key
        String imageCodeValue = dto.getImageCodeValue();
        String phone = dto.getPhone();
        String imageCodeKey = dto.getImageCodeKey();

        //进行校验
        if(StringUtils.isEmpty(imageCodeKey)||StringUtils.isEmpty(phone)||StringUtils.isEmpty(imageCodeValue)){
            throw new BusinessException("信息异常");
        }
        //校验图形验证码，
        //从redis中获取key对应的value
        Object imageCodeTmp = redisTemplate.opsForValue().get(imageCodeKey);

        //判断获取的结果是否为null，
        if(imageCodeTmp == null){
            //为空则表示redis缓存中图形验证码已过期被销毁，返回前端异常。
            throw new BusinessException("图形验证码已过期，请重新获取");
        }
        //获取到图形验证码，还要判断是否正确
        if(!imageCodeTmp.toString().equalsIgnoreCase(imageCodeValue)){
            //不正确就抛异常
            throw new BusinessException("图形验证码输入错误，请重试");
        }

        //图形验证码没问题就要验证手机号了
        //如果没有注册过，则在user表中不会存在此手机号，所以用此判断。
        //使用loadByPhone查询user表，如果为空，则说明没注册过。
        User user = userMapper.loadByPhone(phone);
        //不为空，则说明注册过了，直接让他去登录
        if(user!=null){
            throw new BusinessException("此手机号已注册，请直接登录");
        }

        //使用register:手机号，这种方式拼接key，用以判断redis中是否存在对应的value
        String key = BaseConstant.RegisterConstants.REGISTER_SMS_PREFIX + phone;
        //使用拼接的key去获取redis中的验证码，验证码不一定存在，取决于下面的判断。
        Object smsCodeTmp = redisTemplate.opsForValue().get(key);
        //创建空字符串用于设置验证码
        String smsCode = "";
        //判断smsCodeTmp是否为空
        if(smsCodeTmp!=null){
            //不为空，说明redis中还有对应的验证码
            //判断验证码是否过了一分钟重发事件，添加此判断的意义在于防止不法分子恶意攻击
            //生成value的格式在smsCodeTmp判断为空时设置。格式为“验证码”：“时间戳”。
            //通过:分隔，选取下标为1的元素，也就是时间戳。
            Long oldTime = Long.valueOf(smsCodeTmp.toString().split(":")[1]);
            //通过当前时间减去时间戳，判断时间是否超过一分钟，如果没有超过，则说明不法分子恶意攻击
            if(System.currentTimeMillis()-oldTime <= 1*60*1000){
                throw new BusinessException("老乡，开下门，社区送温暖！！！");
            }
            //时间大于一分钟，小于三分钟，则说明验证码还有效，就使用当前验证码，并且刷新时间戳。
            smsCode =smsCodeTmp.toString().split(":")[0];
        }else {
            //为空，则说明没有对应的value值
            //生成一个新的验证码,指定长度
            smsCode = StrUtils.getComplexRandomString(4);
        }
        //将验证码和key存进redis，并且将验证码拼接时间戳，用于计算过期时间
        String smsCodeValue = smsCode + ":" + System.currentTimeMillis();
        //存进redis
        redisTemplate.opsForValue().set(key,smsCodeValue,3,TimeUnit.MINUTES);
//        SmsUtil.sendSms(phone,"亲，您的验证码是："+smsCode+"。请在3分钟内使用！！");
        System.out.println("亲，您的验证码是："+smsCode+"。请在3分钟内使用！！");
    }
}












